﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Xml.Linq;

namespace Red.Core
{
    public class AggregateStringProvider<T> : IStringProvider<T>
    {
        public ICollection<IStringProvider<T>> Providers { get; private set; } = new List<IStringProvider<T>>();
        
        public T GetValue( string key )
        {
            foreach ( IStringProvider<T> provider in Providers )
            {
                T value = provider.GetValue( key );

                if ( !Equals( value, default( T ) ) )
                {
                    return value;
                }
            }

            return default( T );
        }
    }

    public class XmlStringProvider<T> : StringProvider<T> where T : new()
    {
        #region C-Tor

        public XmlStringProvider( string filename, string xmltag )
        {
            IEnumerable<T> strings = Read<T>( filename, xmltag );
            InitializeDictionary( strings );
        }

        #endregion C-Tor

        #region Private Methods

        private void InitializeDictionary( IEnumerable<T> strings )
        {
            PropertyInfo property = GetKeyProperty();
            if ( strings != null && property != null )
            {
                _strings = strings.ToDictionary( element => GetKey( property, element ) );
            }
            else
            {
                _strings = new Dictionary<string, T>();
            }
        }

        private static PropertyInfo GetKeyProperty()
        {
            Type type = typeof( T );

            PropertyInfo property;

            property = type.GetProperty( "Key" );
            if ( property != null ) return property;

            property = type.GetProperty( "Header" );
            if ( property != null ) return property;

            return null;
        }

        private static string GetKey( PropertyInfo property, T element )
        {
            return (string)property.GetValue( element );
        }

        #endregion Private Methods
    }

    public class StringProvider<T> : StringProvider, IStringProvider<T>
    {
        #region Protected Data

        protected Dictionary<string, T> _strings;

        #endregion Protected Data

        #region Public Methods

        public T GetValue( string key )
        {
            T value = default( T );
            _strings.TryGetValue( key, out value );
            return value;
        }

        #endregion Public Methods
    }

    public interface IStringProvider<out T>
    {
        T GetValue( string key );
    }

    public class StringProvider
    {
        #region Public Methods

        public static IEnumerable<T> Read<T>( string filename, string xmltag ) where T : new()
        {
            string path = Path.Combine( AppDomain.CurrentDomain.BaseDirectory, "Data", filename );

            XDocument document;

            try
            {
                document = XDocument.Load( path );
            }
            catch( FileNotFoundException )
            {
                return null;
            }

            IEnumerable<XElement> elements = document.Descendants( xmltag );
            IEnumerable<T> strs = from element in elements select Create<T>( element );

            return strs;
        }

        #endregion Public Methods

        #region Private Methods

        private static T Create<T>( XElement element ) where T : new()
        {
            T str = new T();
            Type type = typeof( T );

            PropertyInfo[] properties = type.GetProperties();

            foreach ( PropertyInfo property in properties )
            {
                if ( property.PropertyType == typeof( string ) )
                {
                    string value = element.Attribute( property.Name )?.Value.Trim();

                    if ( value != null )
                    {
                        property.SetValue( str, value );
                    }
                }
            }

            PropertyInfo descriptionProperty = type.GetProperty( "Description" );

            if ( descriptionProperty != null && descriptionProperty.GetValue( str ) == null )
            {
                // Remove all whitespace at the beginning and end, and remove all whitespace surrounding any newlines
                descriptionProperty.SetValue( str, Regex.Replace( element.Value, @"[^\S\n]*\n[^\S\n]*", "\n" ).Trim() );
            }

            return str;
        }

        #endregion Private Methods
    }
}
